import $ from '../util/jquery.js';
import { globalConfig, replacePrefix, error } from '../config/config.js';
import util from '../util/util.js';

// 缓存已经加载的外部资源路径，避免不同组件同一个资源重复加载
const LOADED_PATH = [];
// 禁止出现的自定义方法名称
const PROHIBITED_METHOD_NAME = ['$render','$methods','$options'];
// 一个表示成功的jquery延迟对象
const RESOLVE_DEF = $.Deferred();
RESOLVE_DEF.resolve();

/**
 * 获取一个原型链是指定methods对象的一个类
 */
function createRenderObject(methods){
	function RenderObject(){
	}
	let propMethods = $.extend({},methods);
	if (methods) {
		for (let x = 0 ; x < PROHIBITED_METHOD_NAME.length ; x ++) {
			let k = PROHIBITED_METHOD_NAME[x];
			if (propMethods[k]) {
				error("不允许出现[" + k + "]组件方法名，这是一个内置的方法/属性名！");
				delete propMethods[k];
			}
		}
		$.extend(RenderObject.prototype,propMethods)
	}
	RenderObject.prototype.$methods = propMethods;
	return RenderObject;
}

/**
 * 组件类、组件预渲染，预处理结果封装类
 */
class Component {
	/**
	 * @param {Object} html 组件html源代码
	 */
	constructor(html){
		
		// 主脚本
		this.localJs = void 0;
		// 组件模板
		this.template = void 0 ;
		// 模板引擎对象
		this.tmpObject ;
		// 组件配置，通过localJs脚本返回的object获取
		this.config = util.emptyObject();
		// 是否已经执行了初始化
		this.inited = false ;
		// 组件脚本中的this创建类
		this.RenderObject = void 0;
		
		
		let roots = $('<div/>').append($.parseHTML(html, true)).children();
		
		// 避免读取到文本、注释节点
		roots.each((i, root) => {
			let $root = $(root);
			if (root.nodeName == 'SCRIPT') {
				if (this.localJs) {
					error("错误的组件格式，jQuery-SPA要求组件中只能有一个内部执行的script节点。但是却有多个：\n" + html);
					return ;
				}
				this.localJs = root.text || '';
			} else if (root.nodeName == 'STYLE') {
				util.appendToHead(root);
			} else {
				if (this.template) {
					error("错误的组件格式，jQuery-SPA要求组件中只能有一个模板节点。但是却有多个：\n" + html);
					return ;
				}
				this.template = $.trim(root.innerHTML) ;
			}
		});
		
		if (!this.template) {
			error("错误的组件格式，jQuery-SPA要求组件中必须有一个模板节点。但是却未找到或组件内元素为空：\n" + html);
		}
		
		this.tmpObject = globalConfig.template.init.call(void 0,this.template || '') || this.template;
		
	}
	
	/**
	 * 将当前组件渲染到指定dom中(类似jquery.load方法)
	 * @param {Object} $dom 要渲染的jqueryDom
	 * @param {Object} params 父组件传递给当前组件的参数，object类型
	 * @param {Object} queryParam url参数,object类型
	 */
	render($dom,params,queryParam) {
		if (this.inited) {
			if (this.inited === true) {
				this._render($dom,params,queryParam);
				return RESOLVE_DEF;
			} else {
				let def = $.Deferred();
				this.inited.push([def,arguments]);
				return def ;
			}
		} else {
			this.inited = [];
			return this._init().done(() => {
				let rs = this.inited;
				this.inited = true ;
				this._render($dom,params,queryParam);
				if (rs && rs.length) {
					for (let x = 0 ; x < rs.length ; x ++) {
						this._render.apply(this,rs[x][1]);
						setTimeout(() => {
							rs[x][0].resolve();
						},0);
					}
				}
			});
		}
	}
	
	/**
	 * 渲染操作，仅内部调用
	 */
	_render($dom,params,queryParam) {
		if (!($dom && $dom.jquery && $dom.length)) {
			return ;
		}
		$dom = $dom.first();
		let data = typeof this.config.data == 'function' ? this.config.data() : util.emptyObject();
		let props = this.config.props;
		if (props && props.length && (params || queryParam)) {
			for (let x = 0 ; x < props.length ; x ++)  {
				let k = props[x];
				let v = (queryParam && queryParam[k]) || (params && params[k]);
				if (v) {
					data[k] = v;
				}
			}
		}
		let that = new this.RenderObject();
		that.data = data;
		that.$query = queryParam || util.emptyObject();
		that.$options = this.config ;
		that.$el = $dom[0] ;
		that.$jqDom = $dom ;
		
		this._interceptor(that,'created');
		
		var preRenderObject = globalConfig.template.preRender.call(that,this.tmpObject,data);
		
		this._interceptor(that,'beforeMount');
		
		globalConfig.template.render.call(that,preRenderObject,$dom[0],$dom);
		
		let $refs = util.emptyObject();
		$dom.find('[ref]').each(function(i,d){
			$refs[d.getAttribute('ref')] = d ;
		});
		that.$refs = $refs ;
		
		globalConfig.template.beforeMount.call(that,preRenderObject);
		
		this._interceptor(that,'mounted');
	}
	
	/**
	 * 执行拦截器，仅内部调用
	 */
	_interceptor(that,funName) {
		if (typeof this.config[funName] == 'function') {
			this.config[funName].call(that);
		}
	}
	
	/**
	 * 初始化，仅允许内部调用
	 */
	_init() {
		// 初始化组件配置
		if (this.localJs) {
			this.config = new Function("return " + $.trim(this.localJs))() || util.emptyObject();
			util.freeze(this.config);
			let initFunction = this.config.methods && this.config.methods.init;
			if (typeof initFunction === 'function') {
				initFunction.call(void 0);
			}
			this.RenderObject = createRenderObject(this.config.methods);
		}
		let def = $.Deferred();
		if (this.config.require && this.config.require.length) {
			window.require(this.config.require, function(){
				def.resolve();
			});
		} else {
			def.resolve();
		}
		return def;
	}
	
}
export default Component ;
